#include "sourcerow.h"

#include "item.h"

#include "pricedelegate.h"
#include "quantitydelegate.h"
#include "favicongrabber.h"
#include "iconanimators.h"
#include "urlutils.h"

#include <QStandardItem>
#include <QPixmap>
#include <QPainter>

#include <assert.h>

SourceRow::SourceRow(
    Source *source
,   ItemRow *parent)
:   m_source(source)
,   m_parent(parent)
,   m_favIconGrabber(NULL)
,   m_itemIconAnimator(NULL)
{
    assert(source);

    createItems();
    createIconAnimator();
}

void SourceRow::createItems()
{
    assert(m_source);

    m_nameItem  = new QStandardItem(m_source->name());
    m_nameItem->setEditable(true);

    m_nameItem->setCheckable(true);
    m_nameItem->setCheckState(Qt::Checked);

    m_priceItem = new QStandardItem(PriceDelegate::textFromValue(m_source->price()));
    m_priceItem->setData(Qt::AlignRight, Qt::TextAlignmentRole);
    m_priceItem->setData(m_source->price(), PriceDelegate::dataRole());
    m_priceItem->setEditable(true);

    m_quantityItem = new QStandardItem(
        QuantityDelegate::textFromValue(m_source->quantity()));
    m_quantityItem->setData(Qt::AlignRight, Qt::TextAlignmentRole);
    m_quantityItem->setData(m_source->quantity(), QuantityDelegate::dataRole());
    m_quantityItem->setEditable(true);

    if(isValidUrl(m_source->url()))
        m_urlItem = new QStandardItem(QIcon(":/url"), m_source->url().toString());
    else
        m_urlItem = new QStandardItem(m_source->url().toString());
    m_urlItem->setEditable(true);

    // connect

    connect(m_source, SIGNAL(ignoreStateChanged(bool))
        , this, SLOT(source_ignoreStateChanged(bool)));

    connect(m_source, SIGNAL(nameChanged(const QString &))
        , this, SLOT(source_nameChanged(const QString &)));

    connect(m_source, SIGNAL(priceChanged(int))
        , this, SLOT(source_priceChanged(int)));

    connect(m_source, SIGNAL(quantityChanged(uint))
        , this, SLOT(source_quantityChanged(uint)));

    connect(m_source, SIGNAL(urlChanged(const QUrl &))
        , this, SLOT(source_urlChanged(const QUrl &)));


    connect(m_source, SIGNAL(updateStarted())
        , this, SLOT(source_updateStarted()));

    connect(m_source, SIGNAL(updated(bool))
        , this, SLOT(source_updated(bool)));
    connect(m_source, SIGNAL(canceled())
        , this, SLOT(source_canceled()));

    //

    source_urlChanged(m_source->url());
    emit multipleColumnWidthsChanged();
}

SourceRow::~SourceRow()
{
    delete m_itemIconAnimator;

    delete m_nameItem;
    delete m_priceItem;
    delete m_quantityItem;
    delete m_urlItem;
}

QList<QStandardItem*> SourceRow::items() const
{
    return QList<QStandardItem*>()
        << m_nameItem << m_priceItem << m_quantityItem << m_urlItem;
}

const bool SourceRow::hasItemInRow(QStandardItem *item) const
{
    return items().contains(item);
}

const int SourceRow::row() const
{
    return firstColumnItem()->row();
}

QStandardItem *SourceRow::firstColumnItem() const
{
    return m_nameItem;
}

Source *SourceRow::source() const
{
    return m_source;
}

ItemRow *SourceRow::parent() const
{
    return m_parent;
}

void SourceRow::itemChanged(QStandardItem *item)
{
    if(item == m_nameItem)
    {
        if(item->checkState() != (m_source->ignored() ? Qt::Unchecked : Qt::Checked))
            checked_changed();
        else
            name_dataChanged();
    }
    else if(item == m_priceItem)
        price_dataChanged();
    else if(item == m_quantityItem)
        quantity_dataChanged();
    else if(item == m_urlItem)
        url_dataChanged();
}

void SourceRow::checked_changed()
{
    m_source->setIgnored(m_nameItem->checkState() == Qt::Unchecked);
}

void SourceRow::name_dataChanged()
{
    m_source->setName(m_nameItem->text());
}

void SourceRow::price_dataChanged()
{
    m_source->setPrice(m_priceItem->data(PriceDelegate::dataRole()).toInt());
}

void SourceRow::quantity_dataChanged()
{
    m_source->setQuantity(m_quantityItem->data(QuantityDelegate::dataRole()).toUInt());
}

void SourceRow::url_dataChanged()
{
    m_source->setUrl(m_urlItem->text());
}

void SourceRow::source_ignoreStateChanged(bool ignore)
{
    Qt::CheckState state = ignore ? Qt::Unchecked : Qt::Checked;
    if(m_nameItem->checkState() != state)
        m_nameItem->setCheckState(state);

    setEnabled(!ignore);
}

void SourceRow::source_priceChanged(int price)
{
    m_priceItem->setData(price, PriceDelegate::dataRole());
    m_priceItem->setText(PriceDelegate::textFromValue(price));

    const int diff2Prev = source()->differenceToPreviousPrice();

    if(!source()->isValid())
        m_priceItem->setIcon(QIcon());
    else
    {
        if(diff2Prev == 0)
            m_priceItem->setIcon(QIcon(":/tag_green"));
        else
            m_priceItem->setIcon(QIcon(QString(":/tag_green_%1").arg(diff2Prev < 0 ? "drop" : "rise")));
    }
    emit columnWidthChanged(m_priceItem->column());
}

void SourceRow::source_nameChanged(const QString &name)
{
    m_nameItem->setText(name);

    emit columnWidthChanged(m_nameItem->column());
}

void SourceRow::source_quantityChanged(uint quantity)
{
    m_quantityItem->setText(QuantityDelegate::textFromValue(quantity));
    m_quantityItem->setData(quantity, QuantityDelegate::dataRole());

    emit columnWidthChanged(m_quantityItem->column());
}

void SourceRow::source_urlChanged(const QUrl &url)
{
    m_urlItem->setText(url.toString());

    if(isValidUrl(m_source->url()))
    {
        grabFavIcon();
        m_urlItem->setIcon(QIcon(":/url"));
    }
    else
    {
        setIcon(QIcon(":/source_user"));
        m_urlItem->setIcon(QIcon());
    }

    emit columnWidthChanged(m_urlItem->column());
}

void SourceRow::setEnabled(const bool enabled)
{
    if(enabled)
        m_nameItem->setIcon(m_icon);
    else
        m_nameItem->setIcon(m_icon.pixmap(16, QIcon::Disabled));
}

void SourceRow::setToolTip(const QString &tooltip)
{
    foreach(QStandardItem *item, items())
        item->setToolTip(tooltip);
}

void SourceRow::setIcon(const QIcon &icon)
{
    m_icon = icon;
    m_nameItem->setIcon(m_icon);
}

void SourceRow::createIconAnimator()
{
    bool wasActive(false);

    if(m_itemIconAnimator)
        wasActive = m_itemIconAnimator->isActive();

    delete m_itemIconAnimator;
    m_itemIconAnimator = new StandardItemIconAnimator(m_nameItem);

    const QPixmap icon(m_icon.pixmap(16, 16, QIcon::Disabled));

    for(int i = 0; i < 8; ++i)
    {
        QPixmap pixmap(icon);
        QPainter *p = new QPainter(&pixmap);

        p->drawPixmap(0, 0, QPixmap(QString(":/source_update_%1").arg(i)));
        delete p;

        m_itemIconAnimator->appendIcon(pixmap);
    }

    for(int i = 0; i < 4; ++i)
        m_itemIconAnimator->appendIcon(icon);

    if(wasActive)
        m_itemIconAnimator->start();
}

void SourceRow::grabFavIcon()
{
    assert(m_source);

    delete m_favIconGrabber;
    m_favIconGrabber = FavIconGrabber::grab(m_source->url());

    connect(m_favIconGrabber, SIGNAL(grabSucceeded(QIcon&))
        , this, SLOT(favIconGrabber_finished(QIcon&)));
    connect(m_favIconGrabber, SIGNAL(grabFailed(QIcon&))
        , this, SLOT(favIconGrabber_finished(QIcon&)));

    setIcon(QIcon(":/source_null"));

    m_favIconGrabber->start();
}

void SourceRow::favIconGrabber_finished(QIcon &icon)
{
    setIcon(icon);

    delete m_favIconGrabber;
    m_favIconGrabber = NULL;

    createIconAnimator();
}

void SourceRow::source_updateStarted()
{
    assert(!m_itemIconAnimator->isActive());
    m_itemIconAnimator->start();
}

void SourceRow::source_updated(bool succeeded)
{
    if(succeeded && !isValidUrl(m_source->url()))
        return;

    assert(m_itemIconAnimator->isActive());
    m_itemIconAnimator->stop();

    m_urlItem->setIcon(QIcon(succeeded ? ":/url" : ":/url_error"));
    setToolTip(succeeded ? "" : m_source->error());
}

void SourceRow::source_canceled()
{
    if(!isValidUrl(m_source->url()))
        return;

    assert(m_itemIconAnimator->isActive());
    m_itemIconAnimator->stop();

    m_urlItem->setIcon(QIcon(":/url_error"));
    setToolTip("Update was canceled.");
}

